/**

 */
package com.tzstcl.framework.utils;


import com.tzstcl.commons.utils.ExceptionUtils;
import com.tzstcl.commons.utils.IpUtils;
import com.tzstcl.commons.utils.StringUtils;
import com.tzstcl.commons.utils.UserAgentUtils;
import com.tzstcl.sys.monitor.model.SysOperLog;
import com.tzstcl.sys.monitor.service.impl.SysOperLogServiceImpl;
import com.tzstcl.sys.user.model.SysUser;
import com.tzstcl.sys.user.service.impl.SysMenuService;
import eu.bitwalker.useragentutils.UserAgent;
import org.apache.ibatis.mapping.SqlCommandType;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;

import javax.servlet.http.HttpServletRequest;

/**
 * 日志工具类
 *
 */
public class LogUtils {

    /**
     * 静态内部类，延迟加载，懒汉式，线程安全的单例模式
     */
    private static final class Static {
        private static SysOperLogServiceImpl sysOperLogService = SpringUtils.getBean(SysOperLogServiceImpl.class);
        private static SysMenuService menuService = SpringUtils.getBean(SysMenuService.class);
    }

    // 参数名获取工具（尝试获取标注为@ModelAttribute注解的方法，第一个参数名一般为主键名）
    private static ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();

    /**
     * 保存日志
     */
    public static void saveLog(SysUser sysUser, HttpServletRequest request, String logTitle, String logType) {
        saveLog(sysUser, request, null, null, logTitle, logType, 0);
    }

    /**
     * 保存日志
     *
     * @param executeTime
     */
    public static void saveLog(SysUser sysUser, HttpServletRequest request, Object handler, Exception ex, String logTitle, String logType, long executeTime) {

        if (request == null) {
            return;
        }
        SysOperLog log = new SysOperLog();
        log.setLogTitle(logTitle);
        log.setLogType(logType);
        if (StringUtils.isBlank(log.getLogType())) {
            String sqlCommandTypes = (String) request.getAttribute(SqlCommandType.class.getName());
            if (StringUtils.containsAny("," + sqlCommandTypes + ",", ",INSERT,", ",UPDATE,", ",DELETE,")) {
                log.setLogType(SysOperLog.TYPE_UPDATE);
            } else if (StringUtils.contains("," + sqlCommandTypes + ",", ",SELECT,")) {
                log.setLogType(SysOperLog.TYPE_SELECT);
            } else {
                log.setLogType(SysOperLog.TYPE_ACCESS);
            }
        }
        log.setServerAddr(request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort());
        log.setRemoteAddr(IpUtils.getIpAddr(request));
        UserAgent userAgent = UserAgentUtils.getUserAgent(request);
        log.setDeviceName(userAgent.getOperatingSystem().getName());
        log.setBrowserName(userAgent.getBrowser().getName());
        log.setUserAgent(request.getHeader("User-Agent"));
        log.setRequestUri(request.getRequestURI());
        log.setRequestParams(request.getParameterMap());
        log.setRequestMethod(request.getMethod());
        log.setExecuteTime(executeTime);
        if (sysUser != null) {
            log.setCreateBy(sysUser.getId()+"");
            log.setCreateByName(sysUser.getName());
        }
        // 获取异常对象
        Throwable throwable = null;
        if (ex != null) {
            throwable = ExceptionUtils.getThrowable(request);
        }
        log.setIsException(throwable != null ? "1" : "0");
        log.setExceptionInfo(ExceptionUtils.getStackTraceAsString(throwable));

        // 异步保存日志
        new SaveLogThread(log, handler, request.getContextPath(), throwable).start();
    }

    /**
     * 保存日志线程
     */
    public static class SaveLogThread extends Thread {

        private SysOperLog log;
        private Object handler;
        private String contextPath;
        private Throwable throwable;

        public SaveLogThread(SysOperLog log, Object handler, String contextPath, Throwable throwable) {
            super(SaveLogThread.class.getSimpleName());
            this.log = log;
            this.handler = handler;
            this.contextPath = contextPath;
            this.throwable = throwable;
        }

        @Override
        public void run() {

            if (StringUtils.isBlank(log.getLogTitle())) {
                log.setLogTitle("未知操作");
            }

//            // 如果无地址并无异常日志，则不保存信息
//            if (StringUtils.isBlank(log.getRequestUri()) && StringUtils.isBlank(log.getExceptionInfo())) {
//                return;
//            }

            Static.sysOperLogService.add(log);

        }
    }

}
